from functools import wraps

from flask import Blueprint, render_template, make_response, request, session, url_for, redirect, g, abort
from flask_paginate import get_page_parameter, Pagination
from sqlalchemy import func

from apps.cms.models import HighLightArticlr
from apps.cms.models import CMSBanner, CMSBoard, CMSArticle
from apps.utils import resful
from apps import db
from apps.front.forms import SignupForm, SigninForm, AddArticleForm, AddCommentForm
from apps.front.models import FrontUser, FrontComment
from apps.utils.img_captcha import Captcha
from io import BytesIO
from apps.utils import juncache
from apps.utils.safe_url import is_safe_url
from flask.views import MethodView
import config

bp = Blueprint('front', __name__)


# 登录装饰器
def login_required(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if config.FRONT_USER_ID not in session:
            return redirect(url_for('front.signin'))
        return func(*args, **kwargs)

    return wrapper


@bp.route('/')
def index():
    current_board = request.args.get('bd', type=int, default=None)
    sort = request.args.get('sort', type=int, default=1)
    banners = CMSBanner.query.order_by(CMSBanner.priority.desc()).all()
    boards = CMSBoard.query.all()
    # 获取分页参数
    page = request.args.get(get_page_parameter(), type=int, default=1)
    # 设置每页显示帖子数量
    start = (page - 1) * config.PER_PAGE
    end = page * config.PER_PAGE
    # 获取分页控制按钮
    pagination = Pagination(bs_version=3, page=page, total=CMSArticle.query.count())
    if sort:
        if sort == 1:
            query_obj = CMSArticle.query.order_by(CMSArticle.add_time.desc())
        elif sort == 2:
            query_obj = db.session.query(CMSArticle).outerjoin(HighLightArticlr).order_by(CMSArticle.add_time.desc(),HighLightArticlr.add_time.desc())
        elif sort == 3:
            # 暂时未写点赞逻辑
            query_obj = CMSArticle.query.order_by(CMSArticle.add_time.desc())
        elif sort == 4:
            query_obj = db.session.query(CMSArticle).outerjoin(FrontComment).group_by(CMSArticle.id).order_by(func.count(FrontComment.id).desc(),CMSArticle.add_time.desc())
    if current_board:
        articles = query_obj.filter(CMSArticle.board_id==current_board).slice(start, end)
    else:
        articles = query_obj.slice(start, end)
    return render_template('front/front_index.html',
                           banners=banners,
                           boards=boards,
                           pagination=pagination,
                           articles=articles,
                           current_board=current_board,
                           current_sort=sort)


@bp.route('/signup/', methods=['GET', 'POST'])
def signup():
    form = SignupForm(request.form)
    if request.method == 'POST':
        if form.validate():
            data = form.data
            front_user = FrontUser(
                username=data['username'],
                password=data['password1'],
                telephone=data['telephone'],
            )
            db.session.add(front_user)
            db.session.commit()
            return resful.success()
        else:
            print(form.get_error())
            return resful.param_error(message=form.get_error())
    # 判断是否有referer，有的话，注册成功后就跳转到referer指向的url,否则跳转到主页
    return_to = request.referrer
    if return_to and return_to != request.url and is_safe_url(return_to):
        return render_template('front/front_signup.html', return_to=return_to)
    else:
        return render_template('front/front_signup.html', form=form)


class SigninView(MethodView):
    def get(self):
        return_to = request.referrer
        if return_to and return_to != request.url and return_to != url_for('front.signup') and is_safe_url(return_to):
            return render_template('front/front_signin.html', return_to=return_to)
        return render_template('front/front_signin.html')

    def post(self):
        form = SigninForm(request.form)
        if form.validate():
            telephone = form.telephone.data
            password = form.password.data
            remember = form.remember.data
            if remember:
                session.permanent = True
            user = FrontUser.query.filter_by(telephone=telephone).first()
            if not user or not user.check_password(password):
                return resful.param_error(message='用户名或者密码错误')
            session[config.FRONT_USER_ID] = telephone
            return resful.success()
        else:
            return resful.param_error(message=form.get_error())


bp.add_url_rule('/signin/', view_func=SigninView.as_view('signin'))


@bp.route('/logout/')
@login_required
def logout():
    session.pop(config.FRONT_USER_ID)
    return redirect(url_for('front.signin'))

@bp.route('/captcha/')
def create_captcha():
    text, image = Captcha.gene_graph_captcha()
    # 将验证码保存到memcached中
    juncache.set(text.lower(), text)
    out = BytesIO()
    image.save(out, 'png')
    out.seek(0)
    resp = make_response(out.read())
    resp.content_type = 'image/png'
    return resp


@bp.route('/add_article/', methods=['GET', 'POST'])
@login_required
def add_article():
    boards = CMSBoard.query.all()
    if request.method == 'POST':
        form = AddArticleForm(request.form)
        if form.validate():
            board_id = form.board_id.data
            board = CMSBoard.query.filter_by(id=int(board_id)).first()
            if not board:
                return resful.param_error(message='您选择的板块不存在')
            article_title = form.article_title.data
            article_content = form.article_content.data
            article = CMSArticle(
                article_title=article_title,
                article_content=article_content,
                board_id=int(board_id),
                author=g.front_user)
            db.session.add(article)
            db.session.commit()
            return resful.success()
        else:
            return resful.param_error(form.get_error())
    return render_template('front/front_add_article.html', boards=boards)


@bp.route('/article/<article_id>/')
def article_detail(article_id):
    article = CMSArticle.query.get_or_404(article_id)
    if not article:
        abort(404)
    return render_template('front/front_article_detail.html', article=article)


@bp.route('/add_comment/', methods=['POST'])
@login_required
def add_comment():
    form = AddCommentForm(request.form)
    if form.validate():
        content = form.content.data
        article_id = form.article_id.data
        comment = FrontComment(content=content, author=g.front_user, article_id=article_id)
        db.session.add(comment)
        db.session.commit()
        return resful.success()
    else:
        return resful.param_error(message=form.get_error())
